Skip to content

Parse __eh_frame DWARF info on macOS for per-PC frame descriptions#430

Merged
jbachorik merged 33 commits intomainfrom
jb/prof-14088
Apr 9, 2026
Merged

Parse __eh_frame DWARF info on macOS for per-PC frame descriptions#430
jbachorik merged 33 commits intomainfrom
jb/prof-14088

Conversation

@jbachorik
Copy link
Copy Markdown
Collaborator

@jbachorik jbachorik commented Mar 22, 2026

What

Parse raw __eh_frame DWARF CFI data on macOS to provide per-PC frame descriptions for GCC-built libraries, improving stack unwinding accuracy for functions with non-standard frame layouts (leaf functions, -fomit-frame-pointer).

Changes

ddprof-lib/src/main/cpp/dwarf.h / dwarf.cpp

  • Add new DwarfParser constructor accepting a raw (eh_frame, size) pair (no binary-search index).
  • Implement parseEhFrame(): linearly iterates CIE/FDE records, extracts code/data alignment from each CIE, decodes per-PC frame descriptions from FDEs, then qsorts the result.
  • Add _has_z_augmentation field: set when the CIE augmentation string starts with 'z'. The FDE augmentation-data-length LEB field is only present in that case; guarding the skip prevents corrupting the instruction stream for CIEs with empty or non-z augmentation. The parser assumes a single CIE per module — the DWARF spec allows multiple CIEs with different augmentation strings, but macOS binaries compiled by clang always emit one CIE per module and this path is only exercised for macOS __eh_frame sections.
  • Pass detectedDefaultFrame() to setDwarfTable(), matching the Linux path and correctly selecting the clang frame layout on aarch64.
  • Add bounds check in CIE parsing before getLeb()/getSLeb() calls to guard against malformed or truncated __eh_frame data.

ddprof-lib/src/main/cpp/symbols_macos.cpp

  • Detect __eh_frame section in the __TEXT segment during Mach-O parsing.
  • When DWARF_SUPPORTED and the section is present, construct a DwarfParser and call cc->setDwarfTable() with the detected default frame.
  • Add missing Symbols::clearParsingCaches() definition (fixes linker error on macOS).

ddprof-lib/src/test/cpp/dwarf_ut.cpp (new)

  • Four gtest unit tests covering parseEhFrame(): empty section, terminator-only, CIE-only, and CIE+FDE.

Why

On macOS, GCC-built shared libraries (libgcc, Homebrew packages) emit __eh_frame with standard DWARF CFI, but no __unwind_info. Previously the profiler fell back to a single library-wide heuristic frame layout for these libraries. Parsing the DWARF FDE table yields exact CFA/FP/PC rules per instruction address, eliminating unwinding failures at function prologues and epilogues.

JIRA: PROF-14088

@jbachorik jbachorik changed the title PROF-14088 Parse __eh_frame DWARF info on macOS for per-PC frame descriptions Mar 22, 2026
@dd-octo-sts
Copy link
Copy Markdown

dd-octo-sts bot commented Mar 22, 2026

CI Test Results

Run: #24180892588 | Commit: f0c6997 | Duration: 21m 21s (longest job)

All 32 test jobs passed

Status Overview

JDK glibc-aarch64/debug glibc-amd64/debug musl-aarch64/debug musl-amd64/debug
8 - - -
8-ibm - - -
8-j9 - -
8-librca - -
8-orcl - - -
11 - - -
11-j9 - -
11-librca - -
17 - -
17-graal - -
17-j9 - -
17-librca - -
21 - -
21-graal - -
21-librca - -
25 - -
25-graal - -
25-librca - -

Legend: ✅ passed | ❌ failed | ⚪ skipped | 🚫 cancelled

Summary: Total: 32 | Passed: 32 | Failed: 0


Updated: 2026-04-09 09:14:05 UTC

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds macOS support for parsing raw __eh_frame DWARF CFI to produce per-PC frame descriptions, improving unwinding for binaries that don’t provide __unwind_info (e.g., GCC-built libs).

Changes:

  • Introduces a DwarfParser constructor + parseEhFrame() path to parse linear .eh_frame / __eh_frame without an .eh_frame_hdr index.
  • Extends macOS Mach-O parsing to locate __eh_frame and populate CodeCache’s DWARF table; also adds Symbols::clearParsingCaches() for macOS.
  • Adds new gtest unit tests for parseEhFrame().

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.

File Description
ddprof-lib/src/main/cpp/dwarf.h Declares the new raw-eh-frame constructor and parseEhFrame() API.
ddprof-lib/src/main/cpp/dwarf.cpp Implements linear __eh_frame parsing and augmentation handling.
ddprof-lib/src/main/cpp/symbols_macos.cpp Finds __eh_frame in Mach-O __TEXT and wires parsed DWARF into CodeCache.
ddprof-lib/src/test/cpp/dwarf_ut.cpp Adds unit tests exercising the new linear .eh_frame parsing path.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@dd-octo-sts
Copy link
Copy Markdown

dd-octo-sts bot commented Apr 3, 2026

Scan-Build Report

User:runner@runnervmrg6be
Working Directory:/home/runner/work/java-profiler/java-profiler/ddprof-lib/src/test/make
Command Line:make -j4 all
Clang Version:Ubuntu clang version 18.1.3 (1ubuntu1)
Date:Fri Apr 3 14:40:28 2026

Bug Summary

Bug TypeQuantityDisplay?
All Bugs1
Logic error
Stack address stored into global variable1

Reports

Bug Group Bug Type ▾ File Function/Method Line Path Length
Logic errorStack address stored into global variablestackWalker.cppwalkVM84837

@jbachorik jbachorik force-pushed the jb/prof-14088 branch 3 times, most recently from dfe7b83 to 4466cfb Compare April 3, 2026 14:38
@jbachorik jbachorik marked this pull request as ready for review April 3, 2026 14:51
@jbachorik jbachorik requested a review from a team as a code owner April 3, 2026 14:51
@jbachorik jbachorik marked this pull request as draft April 3, 2026 15:43
jbachorik and others added 2 commits April 3, 2026 18:14
Revert static thread_local jmp_buf in walkVM — thread_local access via
__tls_get_addr is not async-signal-safe in a dlopen'd agent. Suppress
core.StackAddressEscape in scan-build instead: the jmp_buf address is
always restored to saved_exception before walkVM returns.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@jbachorik jbachorik marked this pull request as ready for review April 3, 2026 16:49
@jbachorik jbachorik requested a review from Copilot April 7, 2026 10:37
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated 4 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

- Add overflow guard before record_end in parseEhFrame
- Guard _ptr accesses in short CIE body path
- Add bounded getLeb/getSLeb overloads; use in parseEhFrame
- Extract DwarfParser::init() to remove constructor duplication
- Demote _eh_frame/_eh_frame_size to locals in MachOParser::parse()
- Add bounds-guard tests: TruncatedRecord, ShortCieBody, FdeAugDataOverrun
- Document table() ownership, CIE/FDE layout assumptions, EOF handling

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
jbachorik and others added 7 commits April 9, 2026 10:38
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
- Merge all GitLab CI config from java-profiler-build repository
- Register project with ADMS and add octo-sts policies
- Move gitlab/ to .gitlab/ for unified CI structure
- Switch to java-profiler IAM service account and SSM prefix
- Flatten pipeline and fix cross-cutting CI issues
- Gate push pipelines on GitHub PR existence; cancel gracefully when no PR
- Fix ARM runner tags: arch:arm64 instead of runner:apm-k8s-arm-metal

PROF-14208
jbachorik and others added 22 commits April 9, 2026 10:38
Agent-Logs-Url: https://github.com/DataDog/java-profiler/sessions/dce1b1b9-1d7e-4322-90be-421ddc8185c6

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: jbachorik <738413+jbachorik@users.noreply.github.com>
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 4.33.0 to 4.35.1.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](github/codeql-action@b1bff81...c10b806)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 4.35.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [DataDog/dd-octo-sts-action](https://github.com/datadog/dd-octo-sts-action) from 1.0.3 to 1.0.4.
- [Release notes](https://github.com/datadog/dd-octo-sts-action/releases)
- [Commits](DataDog/dd-octo-sts-action@acaa02e...96a2546)

---
updated-dependencies:
- dependency-name: DataDog/dd-octo-sts-action
  dependency-version: 1.0.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps the gradle-minor group with 3 updates: [com.diffplug.spotless:spotless-plugin-gradle](https://github.com/diffplug/spotless), [gradle-wrapper](https://github.com/gradle/gradle) and com.diffplug.spotless.


Updates `com.diffplug.spotless:spotless-plugin-gradle` from 8.3.0 to 8.4.0
- [Release notes](https://github.com/diffplug/spotless/releases)
- [Changelog](https://github.com/diffplug/spotless/blob/main/CHANGES.md)
- [Commits](diffplug/spotless@gradle/8.3.0...gradle/8.4.0)

Updates `gradle-wrapper` from 9.4.0 to 9.4.1
- [Release notes](https://github.com/gradle/gradle/releases)
- [Commits](gradle/gradle@v9.4.0...v9.4.1)

Updates `com.diffplug.spotless` from 8.3.0 to 8.4.0

---
updated-dependencies:
- dependency-name: com.diffplug.spotless:spotless-plugin-gradle
  dependency-version: 8.4.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: gradle-minor
- dependency-name: gradle-wrapper
  dependency-version: 9.4.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: gradle-minor
- dependency-name: com.diffplug.spotless
  dependency-version: 8.4.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: gradle-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [actions/cache](https://github.com/actions/cache) from 5.0.3 to 5.0.4.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](actions/cache@cdf6c1f...6682284)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-version: 5.0.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps the gradle-minor group in /build-logic with 1 update: [com.diffplug.spotless:spotless-plugin-gradle](https://github.com/diffplug/spotless).


Updates `com.diffplug.spotless:spotless-plugin-gradle` from 8.3.0 to 8.4.0
- [Release notes](https://github.com/diffplug/spotless/releases)
- [Changelog](https://github.com/diffplug/spotless/blob/main/CHANGES.md)
- [Commits](diffplug/spotless@gradle/8.3.0...gradle/8.4.0)

---
updated-dependencies:
- dependency-name: com.diffplug.spotless:spotless-plugin-gradle
  dependency-version: 8.4.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: gradle-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
validate-jfr-conformance.sh and its companion files (conformance.yaml,
thresholds.env, validate-jfr.jfrs) were referenced by
.gitlab/dd-trace-integration/run-integration-test.sh but never ported
over during the CI migration (b7410c6).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Include dd-trace-integration pipeline in .gitlab-ci.yml
- Fix ProfilerTestApp.java path (.gitlab/test-apps/ not test-apps/)
- Export DDPROF_COMMIT_BRANCH/SHA from prepare.sh for integration scripts

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace hardcoded amd64 image digest with multi-arch dd-octo-sts-ci-base.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Use Datadog MagicMirror proxy (same as dd-trace-java) when
ORG_GRADLE_PROJECT_mavenRepositoryProxy is set in CI.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- getLeb(end): u32 cast + shift<32 guard to prevent UB
- parseEhFrame: use LINKED_FRAME_CLANG_SIZE for sentinel addRecord
- CodeCache copy ctor: guard malloc/memcpy when dwarf_table_length==0

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@zhengyu123 zhengyu123 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@jbachorik jbachorik merged commit b09781e into main Apr 9, 2026
100 checks passed
@jbachorik jbachorik deleted the jb/prof-14088 branch April 9, 2026 15:51
@github-actions github-actions bot added this to the 1.41.0 milestone Apr 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants